//Copyright (c) 2006-2007 Microsoft Corporation.  All rights reserved
using System;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;

using InvocationFramework.Common.Instrumentation;
using InvocationFramework.Common.Resources;

using InvocationFramework.Adapters.Pipeline;

namespace InvocationFramework.Adapters.Pipeline.Trace
{
    /// <summary>
    /// Filtro de trazabilidad
    /// </summary>
    /// <author>Rodolfo Finochietti 14/11/2005 10.20</author>
    public class TraceFilter : IFilter
    {
        #region Private Vars

        /// <summary>
        /// Source utilizado para escribir las entradas de trace
        /// </summary>
        private TraceSource _source;

        /// <summary>
        /// Identificador del evento de trace
        /// </summary>
        private int _eventId;

        /// <summary>
        /// Expersion regular para filtar por nombre de servicio
        /// </summary>
        private Regex _serviceNameRegExp;

        /// <summary>
        /// Expersion regular para filtar por nombre de operacion
        /// </summary>
        private Regex _operationNameRegExp;

        #endregion

        #region IFilter Members

        /// <summary>
        /// Inicializa el filtro
        /// </summary>
        /// <param name="configuration">Configuracion del filtro</param>
        public void Init( XmlNode configuration )
        {
            try
            {
                //Configuro el filtro
                if ( configuration != null )
                {
                    _source = new TraceSource( configuration.Attributes[ "traceSource" ].Value );
                    _eventId = Convert.ToInt32( configuration.Attributes[ "eventId" ].Value, CultureInfo.InvariantCulture );
                    _serviceNameRegExp = new Regex( configuration.Attributes[ "serviceNameFilterRegExp" ].Value );
                    _operationNameRegExp = new Regex( configuration.Attributes[ "operationNameFilterRegExp" ].Value );
                }
            }
            catch ( Exception ex )
            {
                TraceHelper.Error( ex, Texts.TraceFilterInitError );
                throw;
            }
        }

        /// <summary>
        /// Permite implementar la logica de ejecucion del filtro
        /// </summary>
        /// <param name="state">Estado del pipeline</param>
        /// <returns>Resultado de la ejecucion del filtro</returns>
        public FilterResult Process( PipelineState state )
        {
            if ( state != null )
            {
                string serviceName = state.ExecutionInformation.ServiceName;
                string operationName = state.ExecutionInformation.OperationName;

                //Controlo que el nombre del servicio y de operacion macheen con las regexp definidas y no es asi no se debe tracear
                if ( _serviceNameRegExp.IsMatch( serviceName ) && _operationNameRegExp.IsMatch( operationName ) )
                {
                    //Creo un builder con los datos comunes a todo tipo de log
                    StringBuilder header = new StringBuilder( String.Format( CultureInfo.InvariantCulture, "[{0}] [{1}] Service: '{2}', Operation: '{3}', ", DateTime.Now, state.Pipeline, serviceName, operationName ) );

                    //De acuerdo al tipo de pipeline los datos a tracear son distintos
                    switch ( state.Pipeline )
                    {
                        case PipelineType.Input:
                            WriteInputTrace( state, header );
                            break;
                        case PipelineType.Output:
                            WriteOutputTrace( state, header );
                            break;
                        case PipelineType.Exception:
                            WriteExceptionTrace( state, header );
                            break;
                    }

                    //Libero recursos
                    header = null;
                }
            }

            //Este filtro no interrumpe la cadena bajo ninguna circunstancia
            return FilterResult.Continue;
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Escribe una entrada de trace de input
        /// </summary>
        /// <param name="state">Estado donde se encuentra la informacion a tracear</param>
        /// <param name="header"><seealso cref="StringBuilder"/>  con el header de la entrada de trace</param>
        private void WriteInputTrace( PipelineState state, StringBuilder header )
        {
            header.Append( "Parameters: " );

            //Controlo que la operacion tenga parametros
            if ( state.ExecutionInformation.Parameters == null )
            {
                header.Append( "'null'" );
            }
            else
            {
                //Recorro todo los parametros y escribo sus valores
                foreach ( object parameter in state.ExecutionInformation.Parameters )
                {
                    header.Append( "'" );
                    header.Append( parameter );
                    header.Append( "' " );
                }
            }

            //Grabo la entrada de trace
            _source.TraceData( TraceEventType.Information, _eventId, header.ToString( ) );
        }

        /// <summary>
        /// Escribe una entrada de trace de output
        /// </summary>
        /// <param name="state">Estado donde se encuentra la informacion a tracear</param>
        /// <param name="header"><seealso cref="StringBuilder"/> con el header de la entrada de trace</param>
        private void WriteOutputTrace( PipelineState state, StringBuilder header )
        {
            header.Append( "Result: '" );

            //Controlo que la operacion halla devuelto un resultado
            if ( state.ExecutionInformation.Result == null )
            {
                header.Append( "null'" );
            }
            else
            {
                header.Append( state.ExecutionInformation.Result );
                header.Append( "'" );
            }

            //Grabo la entrada de trace
            _source.TraceData( TraceEventType.Information, _eventId, header.ToString( ) );
        }

        /// <summary>
        /// Escribe una entrada de trace de excepcion
        /// </summary>
        /// <param name="state">Estado donde se encuentra la informacion a tracear</param>
        /// <param name="header"><seealso cref="StringBuilder"/>  con el header de la entrada de trace</param>
        private void WriteExceptionTrace( PipelineState state, StringBuilder header )
        {
            header.Append( "Exception:" );

            //Controlo que la operacion halla devuelto una excepion
            if ( state.ExecutionInformation.InvocationException == null )
            {
                header.Append( "null'" );
            }
            else
            {
                header.Append( Environment.NewLine );
                header.Append( state.ExecutionInformation.InvocationException );
            }

            //Grabo la entrada de trace
            _source.TraceData( TraceEventType.Error, _eventId, header.ToString( ) );
        }

        #endregion
    }
}